<?php
/**
 * SQL importer and execution class
 *
 * @package Fusion Registry
 * @subpackage installer
 * @version $Id: sql_importer.php 4 2008-08-03 01:43:03Z registry.fusion $
 */
class sql_importer
{
    var $ipsclass;

    /**
     * Default settings
     */
    var $read_multiply = 1;
    var $finished = FALSE;
    var $read_limit;
    var $import_handle;
    var $offset;


    /**
     * Setup some dynamic defaults
     */
    function auto_run()
    {
        // We can not read all at once, otherwise we can run out of memory
        $memory_limit = trim( @ini_get('memory_limit') );

        // 2 MB as default
        if ( empty($memory_limit) )
        {
            $memory_limit = 2 * 1024 * 1024;
        }

        // Calculate value of the limit
        if ( strtolower( substr($memory_limit, -1) ) == 'm')
        {
            $memory_limit = (int)substr($memory_limit, 0, -1) * 1024 * 1024;
        }
        elseif ( strtolower( substr($memory_limit, -1) ) == 'k')
        {
            $memory_limit = (int)substr($memory_limit, 0, -1) * 1024;
        }
        elseif ( strtolower( substr($memory_limit, -1) ) == 'g')
        {
            $memory_limit = (int)substr($memory_limit, 0, -1) * 1024 * 1024 * 1024;
        }
        else
        {
            $memory_limit = (int)$memory_limit;
        }

         // Just to be sure
        $this->read_limit = $memory_limit / 4;
    }


    /**
     * Will take file section by section and execute all queries, this is the main function
     */
    function execute_sql_file($file)
    {
        if ( !file_exists($file) )
        {
            return false;
        }

        // Start the counters
        $executed_queries = 0;
        $this->offset = 0;

        // Grab the handle
        $this->import_handle = @fopen($file, 'r');

        // Throw away first line, it's garbage ;)
        @fgets($this->import_handle, 1024);

        $buffer = '';
        $sql = '';
        $start_pos = 0;
        $i = 0;

        while ( !($this->finished && $i >= $len) )
        {
            // Grab the next section of the file
            $data = $this->import_next_section();

            if ( $data === FALSE )
            {
                // subtract data we didn't handle yet and stop processing
                $this->offset -= strlen($buffer);
                break;
            }
            elseif ( $data === TRUE )
            {
                // Handle rest of buffer
            }
            else
            {
                // Append new data to buffer
                $buffer .= $data;

                // Do not parse string when we're not at the end and don't have ; inside
                if ( (strpos($buffer, ';') === FALSE) && !$this->finished ) continue;
            }

            // Current length of our buffer
            $len = strlen($buffer);

            // Grab some SQL queries out of it
            while ( $i < $len )
            {
                // Find first interesting character
                $oi = $i;
                $p1 = strpos($buffer, '\'', $i);
                if ($p1 === FALSE) $p1 = 2147483647;
                $p2 = strpos($buffer, '"', $i);
                if ($p2 === FALSE) $p2 = 2147483647;
                $p3 = strpos($buffer, ';', $i);
                if ($p3 === FALSE) $p3 = 2147483647;
                $p4 = strpos($buffer, '#', $i);
                if ($p4 === FALSE) $p4 = 2147483647;
                $p5 = strpos($buffer, '--', $i);
                if ($p5 === FALSE) $p5 = 2147483647;
                $p6 = strpos($buffer, '/*', $i);
                if ($p6 === FALSE) $p6 = 2147483647;
                $p7 = strpos($buffer, '`', $i);
                if ($p7 === FALSE) $p7 = 2147483647;

                $i = min ($p1, $p2, $p3, $p4, $p5, $p6, $p7);

                if ( $i == 2147483647 )
                {
                    $i = $oi;

                    if ( !$this->finished ) break;

                    // at the end there might be some whitespace...
                    if ( trim($buffer) == '' )
                    {
                        $buffer = '';
                        $len = 0;
                        break;
                    }

                    // We hit end of query, go there!
                    $i = strlen($buffer) - 1;
                }

                // Grab current character
                $ch = $buffer[$i];

                // Quotes
                if ( !(strpos('\'"`', $ch) === FALSE) )
                {
                    $quote = $ch;
                    $endq = FALSE;

                    while ( !$endq )
                    {
                        // Find next quote
                        $pos = strpos($buffer, $quote, $i + 1);

                        // No quote? Too short string
                        if ( $pos === FALSE ) break;

                        // Was not the quote escaped?
                        $j = $pos - 1;
                        while ($buffer[$j] == '\\') $j--;

                        // Even count means it was not escaped
                        $endq = (((($pos - 1) - $j) % 2) == 0);

                        // Skip the string
                        $i = $pos;
                    }

                    if ( !$endq ) break;
                    $i++;

                    // Aren't we at the end?
                    if ( $this->finished && $i == $len )
                    {
                        $i--;
                    }
                    else
                    {
                        continue;
                    }
                }

                // Not enough data to decide
                if ( (($i == ($len - 1) && ($ch == '-' || $ch == '/'))
                     || ($i == ($len - 2) && (($ch == '-' && $buffer[$i + 1] == '-') || ($ch == '/' && $buffer[$i + 1] == '*')))
                     ) && !$this->finished )
                {
                    break;
                }

                // Comments
                if ( $ch == '#'
                     || ($i < ($len - 1) && $ch == '-' && $buffer[$i + 1] == '-' && (($i < ($len - 2) && $buffer[$i + 2] <= ' ')
                     || ($i == ($len - 1) && $this->finished))) || ($i < ($len - 1) && $ch == '/' && $buffer[$i + 1] == '*') )
                {
                    // Copy current string to SQL
                    if ( $start_pos != $i )
                    {
                        $sql .= substr($buffer, $start_pos, $i - $start_pos);
                    }

                    // Skip the rest
                    $i = strpos($buffer, $ch == '/' ? '*/' : "\n", $i);

                    // didn't we hit end of string?
                    if ( $i === FALSE )
                    {
                        if ( $this->finished )
                        {
                            $i = $len - 1;
                        }
                        else
                        {
                            break;
                        }
                    }

                    // Skip *
                    if ( $ch == '/' ) $i++;

                    // Skip last char
                    $i++;

                    // Next query part will start here
                    $start_pos = $i;

                    // Aren't we at the end?
                    if ( $i == $len )
                    {
                        $i--;
                    }
                    else
                    {
                        continue;
                    }
                }

                // End of SQL
                if ( $ch == ';' || ($this->finished && ($i == $len - 1)) )
                {
                    if ( $start_pos < $len )
                    {
                        $sql .= substr($buffer, $start_pos, $i - $start_pos + 1);
                    }

                    if ( ($this->ipsclass->vars['sql_tbl_prefix'] != "ibf_") and !empty($this->ipsclass->vars['sql_tbl_prefix']) )
                    {
                        $sql = preg_replace("/ibf_(\S+?)([\s\.,]|$)/", $this->ipsclass->vars['sql_tbl_prefix']."\\1\\2", $sql);
                    }

                    // Attempt to execute SQL call
                    if ( !$this->ipsclass->DB->query($sql) )
                    {
                        $this->ipsclass->template->install_error($sql."<br /><br />".$this->ipsclass->DB->error);
                    }

                    // Keep track for report
                    $executed_queries++;

                    $buffer = substr($buffer, $i + 1);

                    // Reset parser:
                    $len = strlen($buffer);
                    $sql = '';
                    $i = 0;
                    $start_pos = 0;

                    // Any chance we will get a complete query?
                    if ((strpos($buffer, ';') === FALSE) && !$this->finished) break;
                }
            } // End of parser loop
        } // End of import loop

        return $executed_queries;
    }


    /**
     * To prevent memory limit errors, this function will return just one section
     * of the file at a time.
     */
    function import_next_section($size = 32768)
    {
        // Add some progression while reading large amount of data
        if ( $this->read_multiply <= 8 )
        {
            $size *= $this->read_multiply;
        }
        else
        {
            $size *= 8;
        }

        $this->read_multiply++;

        // We can not read too much
        if ( $size > $this->read_limit )
        {
            $size = $this->read_limit;
        }

        if ( $this->finished )
        {
            return TRUE;
        }

        $result = @fread($this->import_handle, $size);

        $this->finished = @feof($this->import_handle);

        $this->offset += $size;

        return $result;
    }
}

?>